#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>

#define SC_CHECK(expr, msg) \
{ \
   if ((expr) == -1) \
   { \
      perror(msg); \
      exit(EXIT_FAILURE); \
   } \
}

int main(int argc, char *argv[])
{
   char filename[256];
   char *buffer;
   int numPorts;
   int *fds[2];
   struct pollfd *pollFds;
   uint16_t len, readLen, writeLen, tmpLen;
   ssize_t l;
   int i, j;

   if (argc < 3)
   {
      printf("Usage : %s port1 port2 [portn...]\n", argv[0]);
      printf("Links together the different FIFO corresponding to the given ports\n");
      return 0;
   }

   numPorts = argc - 1;
   fds[0] = malloc(numPorts * sizeof(int));
   fds[1] = malloc(numPorts * sizeof(int));
   pollFds = malloc(numPorts * sizeof(struct pollfd));

   //Ouverture des FIFO d'entree et de sortie
   for (i = 0; i < numPorts; i++)
   {
      sprintf(filename, "%s_in", argv[i + 1]);
      printf("Opening %s...\n", filename);
      SC_CHECK((fds[0][i] = open(filename, O_WRONLY)), "open");

      sprintf(filename, "%s_out", argv[i + 1]);
      printf("Opening %s...\n", filename);
      SC_CHECK((fds[1][i] = open(filename, O_RDONLY)), "open");

      pollFds[i].fd = fds[1][i];
      pollFds[i].events = POLLIN;
   }

   printf("All the FIFO are open: the hub is running!\n\n");


   while (1)
   {
      //Attend qu'une FIFO d'entree ait recu un paquet
      SC_CHECK(poll(pollFds, numPorts, -1), "poll");

      for (i = 0; i < numPorts; i++)
      {
         if (pollFds[i].revents == POLLIN)
         {
            //Lit la taille du paquet
            SC_CHECK((read(fds[1][i], &len, 2)), "read");
            len = ntohs(len);

            //Lit le paquet en integralite
            buffer = malloc(len);
            readLen = 0;
            while (len - readLen > 0)
            {
               SC_CHECK((l = read(fds[1][i], buffer + readLen, len - readLen)), "read");
               readLen += l;
            }

            //Ecrit le paquet vers les autres FIFO de sortie
            for (j = 0; j < numPorts; j++)
            {
               if (i == j)
                  continue;

               //Ecrit la taille du paquet
               tmpLen = htons(len);
               SC_CHECK(write(fds[0][j], &tmpLen, 2), "write");

               //Ecrit le paquet en integralite
               writeLen = 0;
               while (len - writeLen > 0)
               {
                  SC_CHECK((l = write(fds[0][j], buffer + writeLen, len - writeLen)), "write");
                  writeLen += l;
               }
            }

            free(buffer);
         }
      }
   }

   return 0;
}
